package com.colin.distributelock.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;

import java.util.Collections;
import java.util.UUID;

/**
 * Created by Colin on 2020/8/31 21:07
 * email: colinzhaodong@gmail.com
 * desc:
 *
 * @author zhaod
 */
@Slf4j
public class RedisLock implements AutoCloseable {
	private RedisTemplate redisTemplate;
	private String key;
	private int expireSeconds;
	private String value;

	public RedisLock(RedisTemplate redisTemplate, String key, int expireSeconds) {
		this.redisTemplate = redisTemplate;
		this.key = key;
		this.expireSeconds = expireSeconds;
		this.value = UUID.randomUUID().toString();
	}

	public Boolean getLock() {
		RedisCallback<Boolean> redisCallback = connection -> {
			RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
			Expiration expiration = Expiration.seconds(expireSeconds);
			byte[] keySerial = redisTemplate.getKeySerializer().serialize(key);
			byte[] valueSerial = redisTemplate.getValueSerializer().serialize(value);
			return connection.set(keySerial, valueSerial, expiration, setOption);
		};

		Boolean execute = (Boolean) redisTemplate.execute(redisCallback);
		if (execute) {
			log.info("获取到了锁!");
		} else {
			log.info("未获取到锁!");
		}
		return execute;
	}

	public Boolean unLock() {
		String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
				"  return redis.call(\"del\",KEYS[1])\n" +
				"else return 0\n" +
				"end;";
		RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class);
		Boolean execute = (Boolean) redisTemplate.execute(redisScript, Collections.singletonList(key), value);
		if (execute) {
			log.info("释放了锁");
		} else {
			log.info("锁释放失败");
		}
		return execute;
	}

	@Override
	public void close() throws Exception {
		unLock();
	}
}
